// VC70SocketThread.cpp: implementation of the CVC70SocketThread class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "Resource.h"

#include "Mmsystem.h"

#include "../RMSLib/RMSWaveClientSocket.h"
#include "../RMSLib/VC70SocketThread.h"
#include "../RMSLib/VideoPlayer.h"
#include "../RMSLib/font8x8.h"

#include "../RMSLib/RMSDirectX.h"

#include "../RMSLib/utility.h"
#include "../Common/CTags.h"

#include "../Logrus/Codec.h"
#include "../Logrus/Sort.h"
#include "../Logrus/BitLine.h"

#include "../RMSTomline/AuthServer/log.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif


//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

#define MAX_CONNECT_RETRY 0xFFFF
#define MIN_FREE_SPACE 0x5
#define SLEEP_CYCLE 0x5

#define MakeSleep(n)  { int d; for(d=0; d<(n*100); d++) { Sleep(10); } }

#define MAXVIDSIZE	0x200000

//#define mydebug

#ifdef mydebug
CMyDebug md4("debug_tcp.log");
#endif
// debug end*/



CVC70SocketThread::CVC70SocketThread()
{
	m_file = NULL;
	m_pData = 0;//new BYTE[MAXVIDSIZE];
	//memset(m_pData,0,MAXVIDSIZE);
	slFile=0;
	timeBegin = 0;
	timeEnd = 0;
	timeFrame = 0;
	dwDelta = 0;
	nFPS = 0;
	m_nUDPServerPort = 0;
	m_Socket = 0;
	m_bDecode = FALSE;
	memset(&m_sa, 0, sizeof(m_sa));
}

void CVC70SocketThread::CloseSocket(BOOL bShowStatus)
{
	CEvent* pevent = getRefreshEvent();
	if(pevent) pevent->SetEvent();
	if(m_Socket)
	{
		if(bShowStatus) UpdateStatus(CLOSING_SOCKET);
		shutdown(m_Socket, SD_BOTH);
		LINGER linger = { 1,0 };
		setsockopt(m_Socket, SOL_SOCKET, SO_LINGER, (LPCTSTR)&linger, sizeof(linger));
		closesocket(m_Socket);
		m_Socket = 0;
	}
}

CVC70SocketThread::~CVC70SocketThread()
{
	Terminate(TRUE);
	if(m_file) fclose(m_file);
	m_file = NULL;
	if(m_pData) delete[] m_pData;
	m_pData = NULL;

	if(slFile && slFile->IsLocked()) slFile->Unlock();

	if(slFile) delete slFile;
	slFile = NULL;
}

void CVC70SocketThread::UpdateStatus(BYTE status, DWORD code/* = 0L*/)
{
	HWND hwnd = getWindow();
	if(hwnd)
	{
		PostMessage(hwnd, WM_UPDATE_SESSION_STATUS, (WPARAM)status, (LPARAM)code);
		Sleep(0);

		LPCTSTR str = _T("undefined");
		switch(status)
		{
			case SOCKET_STATUS_UNDEFINED: str = _T("socket status undefined"); break;
			case OPENING_CONNECTION: str = _T("trying to bind() a socket..."); break;
			case CONNECTING: str = _T("trying to connect() to server/encoder..."); break;
			case CONNECTED: str = _T("successfully connected to server/encoder"); break;
			case RECEIVING: str = _T("connection accepted, ready to receive data"); break;
			case CLOSING_SOCKET: str = _T("socket closing..."); break;
			case SESSION_ERROR: str = _T("socket error"); break;
			case BAD_FORMAT: str = _T("bad format of data"); break;
			case CONNECTION_REFUSED: str = _T("connection refused"); break;
			case CONNECTION_LOST: str = _T("no more attempts to connect allowed"); break;
			case SENDING_REQUEST: str = _T("preparing and sending a request to encoder..."); break;
			case UNABLE_TO_BUFFER_DATA: str = _T("unable to open a file to play it back"); break;
			case DISCONNECT: str = _T("stop the playback because of low disk space"); break;
			case FINISH: str = _T("thread terminated"); break;
			case CLOSING_CONNECTION: str = _T("closing connection..."); break;
			case FAIL: str = _T("failed to continue"); break;
			case DECLINE: str = _T("decline"); break;
		}
		mesOut(_T("VC70SocketThread.cpp"), __LINE__, _T("UpdateStatus() to %i(%s), err=%i"), (int)status, str, code);
	}
}

#ifdef _RMS_TOML_
#include <clientthr.h>
#endif

int CVC70SocketThread::ReceiveData(SOCKET* Socket, void* data, int Len)
{
	int i = 0;

	if(!Socket) return -1;
	if(!*Socket) return -1;
	if(!data) return 0;

	while(i < Len)
	{
		int n = recv(*Socket, (char*)data + i, Len - i, 0);
//		int n = recvtimeout(*Socket, (char*)data + i, Len - i, 2);
		
		if(n == SOCKET_ERROR)
		{
			UpdateStatus(SESSION_ERROR, WSAGetLastError());
			CloseSocket();
			return -1;
		}
//		if((n == -2) || (n == 0))
		if(n == 0)
		{
			return -1;
		}
		i += n;
	}

	if(!m_bStored)
	{
		m_lTotalReceivedData += i;
	}
#ifdef RSAPROTECT
    EnterCriticalSection(&r1CS);
    gMeasure += i;
    LeaveCriticalSection(&r1CS);
#endif
	return i;
}

BOOL CVC70SocketThread::TAGReceive()
{
	DWORD dwLen;
	DWORD dwTag;
	BYTE pac;

	dwTag = 0;
	if(ReceiveData(&m_Socket, &dwTag, 4) < 0)
	{
		return FALSE;
	}
#ifdef mydebug
char tmp[5];tmp[4]=0;memcpy(tmp, &dwTag, 4);
md4.printstr(string("tag: ")+tmp);
#endif

	switch(dwTag)
	{
	case TAG_STOREDDATA:
		slFile->Lock();
		fwrite(&dwTag, sizeof(DWORD), 1, m_file);
		slFile->Unlock();

		m_dwCountByte += sizeof(DWORD);
		break;
	case TAG_TIMEMARK:
		if(ReceiveData(&m_Socket, m_pData, sizeof(TimeMark)) < 0)
		{
			return FALSE;
		}
		slFile->Lock();
		fwrite(&dwTag, sizeof(DWORD), 1, m_file);
		fwrite(m_pData, sizeof(TimeMark), 1, m_file);
		slFile->Unlock();
		m_dwCountByte += sizeof(TimeMark) + sizeof(DWORD);
		break;

	case TAG_RMSSTOREDVIDEO_v01:
		dwLen = 0;
		if(ReceiveData(&m_Socket, &dwLen, 4) < 0)
		{
			return FALSE;
		}
		if(ReceiveData(&m_Socket, m_pData, dwLen) <= 0)
		{
			return FALSE;
		}
		slFile->Lock();
		fwrite(&dwTag, sizeof(DWORD), 1, m_file);
		fwrite(&dwLen, sizeof(DWORD), 1, m_file);
		fwrite(m_pData, sizeof(BYTE), dwLen, m_file);
		slFile->Unlock();
		m_dwCountByte += sizeof(DWORD)*2 + dwLen;
		m_frames++;
		break;
	case TAG_RMSSTOREDSOUND_v01:
		dwLen = 0;
		if(ReceiveData(&m_Socket, &dwLen, 4) < 0)
		{
			return FALSE;
		}
		if(ReceiveData(&m_Socket, m_AudioData, dwLen) <= 0)
		{
			return FALSE;
		}
		slFile->Lock();
		fwrite(&dwTag, sizeof(DWORD), 1, m_file);
		fwrite(&dwLen, sizeof(DWORD), 1, m_file);
		fwrite(m_AudioData, sizeof(BYTE), dwLen, m_file);
		slFile->Unlock();
		m_dwCountByte += sizeof(DWORD)*2 + dwLen;
		break;

	case TAG_RMSVIDEO_v01:
	case TAG_RMSVIDEO_v02:
		dwLen = 0;
		if(ReceiveData(&m_Socket, &dwLen, 4) < 0)
		{
			return FALSE;
		}

		timeFrame = 1000/m_info.m_iFrameRate;
		
		if(ReceiveData(&m_Socket, m_pData, dwLen) <= 0)
		{
			return FALSE;
		}
		if(dwLen > 0)
		{
			BOOL VideoFormatChange = FALSE;

			enterCritical();

			m_bDecode = TRUE;

            static int frames_xx = 0;
            bool bDecodeQuad = false;

            if(dwTag == TAG_RMSVIDEO_v01)
			{
                if(m_info.m_iWidth == 640) { frames_xx = 0; VideoFormatChange = TRUE; }
                m_info.m_iWidth = 320;
                m_info.m_iHeight = 240;
                //   bDecodeQuad = true;
            }

            if(dwTag == TAG_RMSVIDEO_v02)
			{
                if(m_info.m_iWidth == 320) { frames_xx = 0; VideoFormatChange = TRUE; }
                m_info.m_iWidth = 640;
                m_info.m_iHeight = 480;
//                if((getFrameSize() != FS_FULLSCREEN) &&
//				   (getFrameSize() != FS_640x480))
//                    bDecodeQuad = true;
            }

			// re-allocated video buffers on format chanhe
			BYTE** py = getYComponent();
			BYTE** pu = getUComponent();
			BYTE** pv = getVComponent();
			if(py && pu && pv)
			{
				if(VideoFormatChange)
				{
					if(*py) delete *py;
					if(*pu) delete *pu;
					if(*pv) delete *pv;
					*py = new BYTE[m_info.m_iWidth*m_info.m_iHeight];
					*pu = new BYTE[m_info.m_iWidth*m_info.m_iHeight/2];
					*pv = new BYTE[m_info.m_iWidth*m_info.m_iHeight/2];

					// clear it up
					memset(*py, 140, m_info.m_iWidth*m_info.m_iHeight);
					memset(*pu, 128, m_info.m_iWidth*m_info.m_iHeight/2);
					memset(*pv, 128, m_info.m_iWidth*m_info.m_iHeight/2);
				}
				else
				{
					BYTE* decmem = getDecoderMemory();
					if(decmem)
					{
						if(bDecodeQuad)
						{
							DECOMPRESS_QUART_FRAME(*py, *pu, *pv,
												   m_info.m_iWidth, m_info.m_iHeight,
												   m_pData, decmem, dwLen);
						}
						else
						{
							DECOMPRESS_FRAME(*py, *pu, *pv,
											 m_info.m_iWidth, m_info.m_iHeight,
											 m_pData, decmem);
						}
					}
				}
			}

			leaveCritical();

			CEvent* pevent;
			while(!m_bQuit)
			{
				pevent = getRefreshEvent();
				if(!m_bQuit && pevent)
				{
					if(::WaitForSingleObject(*pevent, 1000) == WAIT_OBJECT_0)
						break;
				}
			}

			if(m_bQuit) return 0;
			pevent = getRefreshEvent();
			if(pevent) pevent->ResetEvent();
			HWND hwnd = getWindow();
			if(hwnd)
			{
				SendMessage(hwnd, WM_TIMER, TIMER_REFRESH_DATA, 0L);
			}
			if(m_bQuit) return 0;

			if(dwLen>MAXVIDSIZE) dwLen=MAXVIDSIZE;
			memset(m_pData,0,dwLen);
		}
		m_bDecode = FALSE;
		timeEnd = timeGetTime();
		if(timeFrame > (timeEnd - timeBegin))
		{
			Sleep(timeFrame - (timeEnd - timeBegin));
			dwDelta += timeGetTime() - timeBegin;
		}
		else
		{
			timeFrame = timeEnd - timeBegin;
			dwDelta += timeGetTime() - timeBegin;
		}
		timeBegin = timeGetTime();

		nFPS++;
		if(dwDelta > 1000)
		{
			m_nCurrentFrameRate = (int)(((float)nFPS * 1000.0) / (float)dwDelta + .5);
			m_nBitrate = (int)(((((float)m_lTotalReceivedData * 8.0) / 1024.0)) * ( 1000.0 / (float)dwDelta));
			nFPS = 0;
			m_lTotalReceivedData = 0;
			dwDelta = 0;
		}
		break;
	case TAG_RMSSOUND_v01:
		dwLen = 0;
		if(ReceiveData(&m_Socket, &dwLen, 4) < 0)
		{
			return FALSE;
		}

		if(ReceiveData(&m_Socket, m_pData, dwLen) <= 0)
		{
			return FALSE;
		}
		break;
	case TAG_STOREDTIME_v01:
		dwLen = 0;
		if(ReceiveData(&m_Socket, &dwLen, 4) < 0)
		{
			return FALSE;
		}

		if(dwLen > 50)
		{
			UpdateStatus(BAD_FORMAT);
			Sleep(500);
			return FALSE;
		}
		if(ReceiveData(&m_Socket, m_szTime, dwLen) <= 0)
		{
			return FALSE;
		}
		slFile->Lock();
		fwrite(&dwTag, sizeof(DWORD), 1, m_file);
		fwrite(&dwLen, sizeof(DWORD), 1, m_file);
		fwrite(m_szTime, sizeof(BYTE), dwLen, m_file);
		slFile->Unlock();
		m_dwCountByte += sizeof(DWORD)*2 + dwLen;
		break;
	case TAG_TIME_v01:
		dwLen = 0;
		if(ReceiveData(&m_Socket, &dwLen, 4) < 0)
		{
			return FALSE;
		}

		if(dwLen > 50)
		{
			UpdateStatus(BAD_FORMAT);
			Sleep(500);
			return FALSE;
		}
		if(ReceiveData(&m_Socket, m_szTime, dwLen) <= 0)
		{
			return FALSE;
		}
		enterCritical();
		setSessionTime(m_szTime);
		leaveCritical();
		break;
	case TAG_STOREDDATE_v01:
		dwLen = 0;
		if(ReceiveData(&m_Socket, &dwLen, 4) < 0)
		{
			return FALSE;
		}

		if(dwLen > 50)
		{
			UpdateStatus(BAD_FORMAT);
			Sleep(500);
			return FALSE;
		}
		if(ReceiveData(&m_Socket, m_szDate, dwLen) <= 0)
		{
			return FALSE;
		}
		slFile->Lock();
		fwrite(&dwTag, sizeof(DWORD), 1, m_file);
		fwrite(&dwLen, sizeof(DWORD), 1, m_file);
		fwrite(m_szDate, sizeof(BYTE), dwLen, m_file);
		slFile->Unlock();
		m_dwCountByte += sizeof(DWORD)*2 + dwLen;
		break;
	case TAG_DATE_v01:
		dwLen = 0;
		if(ReceiveData(&m_Socket, &dwLen, 4) < 0)
		{
			return FALSE;
		}

		if(dwLen > 50)
		{
			UpdateStatus(BAD_FORMAT);
			Sleep(500);
			return FALSE;
		}
		if(ReceiveData(&m_Socket, m_szDate, dwLen) <= 0)
		{
			return FALSE;
		}
		enterCritical();
		setSessionDate(m_szDate);
		leaveCritical();
		break;
	default:
		dwLen = 0;
		if(ReceiveData(&m_Socket, &dwLen, 4) < 0)
		{
			return FALSE;
		}

		if(ReceiveData(&m_Socket, m_pData, dwLen) <= 0)
		{
			return FALSE;
		}
	}

	return TRUE;	
}

BOOL CVC70SocketThread::TAGRequest()
{
	int n = 0;
	DWORD dwTag = TAG_GETDATA;

	n = send(m_Socket, (char*)&dwTag, 4, 0);
	if(n == SOCKET_ERROR)
	{
		return FALSE;
	}
	if(n != 4)
	{
		return FALSE;
	}
	return TRUE;
}

/**
 *	returns:
 *		-1, when got the SOCKET_ERROR on send/receive data;
 *		 0, when got answer with BADF tag and the sending
 *			sole 0 byte was successful;
 *		+1, connection was refused by user (through "Decline"
 *			button) or programmatically when password
 *			validation failed;
 **/
DWORD CVC70SocketThread::RequestFormat()
{
	DWORD* Request;
	DWORD dwLen;

	m_bSupportRing=0;
	int nLenPass = strlen(m_info.m_strPassword)+1;

	DWORD dwTag;

	// REQUEST
	if(!m_bStored)
	{ // Live
		Request = (DWORD*)m_pData;
		DWORD* requestStart=Request;
		int	  tagCount=0;
		dwLen = sizeof(DWORD)*15 + nLenPass;
		*Request++ = TAG_REQUEST;
		*Request++ = 6;

		*Request++ = TAG_RMSVIDEO_v01;tagCount++;
#ifdef _NET_VC_
		*Request++ = 1;
#else //def _NET_VC_
		*Request++ = 0;
#endif //def _NET_VC_
		*Request++ = TAG_TIME_v01;tagCount++;
		*Request++ = 0;
		*Request++ = TAG_DATE_v01;tagCount++;
		*Request++ = 0;
		*Request++ = TAG_RMSSOUND_v01;tagCount++;
		*Request++ = 0;
		*Request++ = TAG_CAMERA_v01;tagCount++;
		*Request++ = sizeof(DWORD);
		*Request++ = m_info.m_iID;

		// ---- send - support ring to the server
		*Request++ = TAG_SUPPORT_RING;tagCount++;
		*Request++ = (DWORD)(getEnableRingFlag());
//		*Request++ = 0;

		DWORD* b = Request;
		{
			*Request++ = TAG_VC_RING; tagCount++;
			TCHAR replyAddr[1024];
			if((getCallString(replyAddr, sizeof(replyAddr)) == NULL) ||
			   (strlen(replyAddr) == 0))
			{ // put some dummy reply addr
				strcpy(replyAddr, "Demo User Unknown Unknown 10 demo 127.0.0.1 777 15 0 500 320 240");
			}
			// substitute some parameters in call string
			reformatCallString(replyAddr, &m_info);
#ifdef _TEC_
			int enc = isEncoderActive() ? 1 : 0;
			char encs[10];
			strcat(replyAddr, " ");
			strcat(replyAddr, itoa(enc, encs, 10));
#endif //def _TEC_

			int slen = strlen(replyAddr) + 1;
			if(slen > 1)
			{
				int len = slen; if(len&3) len = (len+3) >> 2 << 2;
				*Request++ = len;
				memcpy(Request, replyAddr, slen);
				Request += len >> 2;
			}
		}

		m_nUDPServerPort = 0;

		DWORD* c = Request;

		*Request++ = TAG_PASSWORD_v01;tagCount++;
		*Request++ = nLenPass;
		strcpy(reinterpret_cast<char*>(Request), m_info.m_strPassword);

		requestStart[1]=tagCount;
		dwLen=(Request-requestStart)*sizeof(DWORD)+nLenPass;

		CString fmt;
		DWORD* a = requestStart;
		fmt.Format(_T("RequestFormat(): %%.4s %%i %%.4s %%i %%.4s %%i "
					  "%%.4s %%i %%.4s %%i %%.4s %%i %%i %%.4s %%i "
					  "%%.4s %%i %%.%is %%.4s %%i %%.%is"), b[1], c[1]);
		mesOut(_T("VC70SocketThread.cpp"), __LINE__, fmt,
			   &a[0], a[1], &a[2], a[3], &a[4], a[5], &a[6], a[7],
			   &a[8], a[9], &a[10], a[11], a[12], &a[13], a[14],
			   &b[0], b[1], &b[2], &c[0], c[1], &c[2]);

		if(send(m_Socket,(char*)requestStart,dwLen,0) == SOCKET_ERROR)
		{
			return (DWORD)-1;
		}

		dwTag = TAG_CONFWAIT;
		while(dwTag == TAG_CONFWAIT)
		{
			if(ReceiveData(&m_Socket, &dwTag, 4) < 0)
			{
				return (DWORD)-1;
			}
			if(m_bQuit) return (DWORD)-1;
		}

		if(dwTag != TAG_ANSWER)
		{
			return (DWORD)-1;
		}

		if(ReceiveData(&m_Socket, &dwTag, 4) < 0)
		{
			return (DWORD)-1;
		}

		// check if it's new server or encoder with ring support
		if(dwTag == TAG_SUPPORT_RING)
		{
			m_bSupportRing = TRUE;
			if(ReceiveData(&m_Socket, &dwTag, 4) < 0) return (DWORD)-1;
		}

		if(dwTag == TAG_GOODFORMAT)
		{
			return (DWORD)1;
		}
		else if(dwTag == TAG_SOUNDUDPPORT)
		{
			if(ReceiveData(&m_Socket, &m_nUDPServerPort, 4) < 0)
			{
				return (DWORD)-1;
			}

			if(ReceiveData(&m_Socket, &dwTag, 4) < 0)
			{
				return (DWORD)-1;
			}
			if(dwTag == TAG_GOODFORMAT)
			{
				return (DWORD)1;
			}
			else
			{
				m_pData[0] = 0;
				if(send(m_Socket, (char*)m_pData, sizeof(BYTE), 0) == SOCKET_ERROR)
				{
					return (DWORD)-1;
				}
				UpdateStatus(BAD_FORMAT);
			}
		}
		else
		{
			m_pData[0] = 0;
			UpdateStatus(CONNECTION_REFUSED);
			if(send(m_Socket, (char*)m_pData, sizeof(BYTE), 0) == SOCKET_ERROR)
			{
				return (DWORD)-2;
			}
			return (DWORD)-2;
		}
	}
	else
	{
#ifdef VC70
		// Stored
		dwLen = sizeof(DWORD)*17 + sizeof(TimeMark)*2 + nLenPass;
		Request = (DWORD*)m_pData;
		*Request++ = TAG_REQUEST;
		*Request++ = 7;

		*Request++ = TAG_STOREDDATA;
		*Request++ = 0;
		*Request++ = TAG_RMSSTOREDVIDEO_v01;
		*Request++ = 0;
		*Request++ = TAG_STOREDTIME_v01;
		*Request++ = 0;
		*Request++ = TAG_STOREDDATE_v01;
		*Request++ = 0;
		*Request++ = TAG_CAMERA_v01;
		*Request++ = sizeof(DWORD);
		*Request++ = m_info.m_iID;
		*Request++ = TAG_PASSWORD_v01;
		*Request++ = nLenPass;
		strcpy(reinterpret_cast<char*>(Request), m_info.m_strPassword);
		Request = reinterpret_cast<DWORD*>((reinterpret_cast<BYTE*>(Request)) + nLenPass);

		*Request++ = TAG_TIMEMARK;
		*Request++ = sizeof(TimeMark)*2;

		*((TimeMark*)Request) = m_timeStart;
		*((TimeMark*)(((BYTE*)Request)+sizeof(TimeMark))) = m_timeEnd;

#ifdef _JNC_REC_
		Request += sizeof(TimeMark)*2/sizeof(DWORD);
		if(m_bGetRecords)
		{
			*Request++ = TAG_GETRECORDS;
			*Request++ = 0;
			dwLen += 2*sizeof(DWORD);
		}
		else
		{
			*Request++ = TAG_TIMEMARK1;
			*Request++ = sizeof(TimeMark)*2;
			*((TimeMark*)Request) = m_timeFrom;
			*((TimeMark*)(((BYTE*)Request)+sizeof(TimeMark))) = m_timeTo;
			dwLen += 2*(sizeof(TimeMark)+sizeof(DWORD));
		}
		*((DWORD*)m_pData + 1) = 8; // number of tags in request
#endif //def _JNC_REC_

		Request = (DWORD*)m_pData;
		
		if(send(m_Socket, (char*)Request, dwLen, 0) == SOCKET_ERROR)
		{
			return (DWORD)-1;
		}

		if(ReceiveData(&m_Socket, &dwTag, 4) < 0)
		{
			return (DWORD)-1;
		}
		if(dwTag != TAG_ANSWER)
		{
			return (DWORD)-1;
		}

		if(ReceiveData(&m_Socket, &dwTag, 4) < 0)
		{
			return (DWORD)-1;
		}
		if(dwTag == TAG_GOODFORMAT)
		{
			CVideoPlayer* pvp = getPlayer();
			if((pvp == NULL) || ReceiveData(&m_Socket, &(pvp->m_lTotalStoredData), sizeof(long)) < 0)
			{
				return (DWORD)-1;
			}
			return (DWORD)1;
		}
		else
		{
			m_pData[0] = 0;
			UpdateStatus(CONNECTION_REFUSED);
			if(send(m_Socket, (char*)m_pData, sizeof(BYTE), 0) == SOCKET_ERROR)
			{
				return (DWORD)-1;
			}
			return (DWORD)-2;
		}
#endif //def VC70
	}

	return (DWORD)0;
}

void CVC70SocketThread::ShowSockError(HWND wnd, const char* msg, const char* IP)
{
	errOut(_T("VC70SocketThread.cpp"), __LINE__, _T("TCP/IP Error: %s IP=%s\nWSA=%#x\n"),
		   msg, IP, WSAGetLastError());
}

BOOL CVC70SocketThread::Execute()
{
	if(m_pData == 0)
	{
		m_pData = new BYTE[MAXVIDSIZE];
		memset(m_pData, 0, MAXVIDSIZE);
	}

	return CRMSThread::Execute();
}

DWORD CVC70SocketThread::tcpRing()
{
	// send ring
	if(getEnableRingFlag())
	{
		TCHAR replyAddr[1024];
		if(getCallString(replyAddr, sizeof(replyAddr)))
		{
			int slen = strlen(replyAddr) + 1;
			if(slen > 1)
			{
				DWORD dwLen;
				DWORD data[256];
				DWORD* Request = data;
				reformatCallString(replyAddr, &m_info);
				slen = strlen(replyAddr) + 1;
				int len = (slen & 3) ? ((slen + 3) >> 2 << 2) : slen;
				*Request++ = TAG_VC_RING;
				*Request++ = len;
				memcpy(Request, replyAddr, slen);
				Request += len >> 2;
				dwLen = (Request - data) * sizeof(DWORD);
				if(send(m_Socket, (char*)data, dwLen, 0) == SOCKET_ERROR)
				{
					return (DWORD)-1;
				}
			}
		}
	}
	return 0;
}

// include "log.h" move to line 23 to resolve compile error.
// for debug only
// #if 0
//#include "../RMSTomline/AuthServer/log.h"
//CLog myLog("debug.log");
//CLog* pLog = &myLog;
// #endif // #if 0

DWORD CVC70SocketThread::ThreadFunc()
{
	mesOut(_T("VC70SocketThread.cpp"), __LINE__, _T("entering ThreadFunc(), m_bQuit=%i"), m_bQuit);

	int cycle;
	int count = 0;
	int errcnt = 1;
	DWORD n, colTags;
	cycle = 0;

	BYTE  **py=0,** pu=0,** pv=0;
	//SetThreadPriority(m_hThread,THREAD_PRIORITY_ABOVE_NORMAL);

#ifdef VC70
	if(!slFile)
	{
		CSyncObject* flock = getFileLock();
		slFile = (flock) ? new CSingleLock(flock) : NULL;
	}
	if(slFile && slFile->IsLocked()) slFile->Unlock();

	CVideoPlayer* pvp = getPlayer();
	if(pvp) pvp->m_lCurrentStoredData = 0;
	m_frames = 0;
#endif //def VC70

	enterCritical();
	if(m_info.m_iWidth < 320) m_info.m_iWidth = 320;
	if(m_info.m_iHeight < 240) m_info.m_iHeight = 240;

	py = getYComponent();
	if(*py) delete *py;
	*py = new BYTE[m_info.m_iWidth * m_info.m_iHeight];
	if(*py == 0) { leaveCritical(); goto error_exit; }
	memset(*py, 140, m_info.m_iWidth * m_info.m_iHeight);

	pu = getUComponent();
	if(*pu) delete *pu;
	*pu = new BYTE[m_info.m_iWidth * m_info.m_iHeight / 2];
	if(*pu == 0) { leaveCritical(); goto error_exit; }
	memset(*pu, 0x80, m_info.m_iWidth * m_info.m_iHeight / 2);

	pv = getVComponent();
	if(*pv) delete *pv;
	*pv = new BYTE[m_info.m_iWidth * m_info.m_iHeight / 2];
	if(*pv == 0) { leaveCritical(); goto error_exit; }
	memset(*pv, 0x80, m_info.m_iWidth * m_info.m_iHeight / 2);
	leaveCritical();

open_connection:
	#define CheckExit()   { if(m_bQuit) goto exit; }
	#define MakeSleep1()  { int d; for(d = 0; d < 100; d++) { Sleep(10); CheckExit(); } }

_log(strtime()<<"open connection!\n");

	CheckExit();
	if((cycle++) > SLEEP_CYCLE) 
	{
		UpdateStatus(CONNECTION_LOST);
		goto error_exit1;
	}

	// bind
	UpdateStatus(OPENING_CONNECTION);

	if(!bindSocket(m_info.getAddress(), m_info.m_port))
		goto error_exit2;

	// connect
	UpdateStatus(CONNECTING);

	if(!connectToServer(m_info.getAddress(), m_info.m_port))
		goto error_exit3;

	UpdateStatus(CONNECTED);

	// protocol
	UpdateStatus(SENDING_REQUEST);
	
	n = RequestFormat();
	if(n != 1)
	{
		CheckExit();
		if((n == 0) || (n == 0xFFFFFFFE))
		{
			if(n == 0xFFFFFFFE)
			{
				UpdateStatus(CONNECTION_REFUSED);
			}
			goto error_exit4;
		}
		else
		{
			CloseSocket(FALSE);
			MakeSleep1(); //wait for 1 second before trying again
			goto open_connection;
		}
	}

	// try to get returned address: tag + address
	DWORD answ[4];
	if(!m_bStored && (recvtimeout(m_Socket, (char*)answ, sizeof(answ), 2) != -2))
	{
//		if((answ[0] == 1) &&
		if((answ[0] == 0xFFFFFFFF) &&
		   (answ[1] == TAG_ADDRESS_ON_RING) &&
		   (answ[2] == sizeof(DWORD)))
		{
			answ[3] = answ[3] ^ (DWORD)CInfo::KEY;
			if((answ[3] != 0) && (m_info.getAddress() != answ[3]))
			{
				// fake response
				DWORD dwReject = TAG_BADFORMAT;
				send(m_Socket, (char*)&dwReject, sizeof(DWORD), 0);
				// close current connection and connect to the new address
				recvtimeout(m_Socket, (char*)answ, sizeof(answ), 1);
				CloseSocket(FALSE);
				enterCritical();
				m_info.setNewAddress(answ[3]);
				leaveCritical();
				MakeSleep1(); //wait for 1 second before trying again
				goto open_connection;
			}
		}
	}

	if(m_bStored)
	{
		if(m_file)
		{
			fclose(m_file);
			m_file = NULL;
		}

		CString hp = getHomePath();
		hp += "\\";
		hp += STORED_VIDEO_FILENAME;
		m_file = fopen(hp, "wb");

		if(!m_file)
		{
			UpdateStatus(UNABLE_TO_BUFFER_DATA);
			goto error_exit5;
		}
	}

#ifdef _JNC_REC_
	if(m_bGetRecords) UpdateStatus(SOCKET_STATUS_UNDEFINED);
	else
#endif //def _JNC_REC_
		UpdateStatus(RECEIVING);
	

	if(!m_bStored && m_nUDPServerPort)
	{
		in_addr a;
		a.S_un.S_addr = m_info.getAddress();
		CRMSWaveClientSocket* pws = (CRMSWaveClientSocket*)getSessionWaveSocket();
		if(pws)
		{
			pws->StartWaveSession(getClient(), inet_ntoa(a), m_nUDPServerPort);
			Sleep(10);
		}
	}

	if(!TAGRequest())
	{
		if(!m_bStored)
		{
			CheckExit();
			CloseSocket(FALSE);
		 	MakeSleep1();
			goto open_connection;
		}
		goto error_exit6;
	}

	if(m_bStored)
	{
		if(!TestDiskSpace())
		{
			UpdateStatus(DISCONNECT);
			goto error_exit7;
		}
	}

/*
	//-------- send ring if it's new server encoder on the other end
	if (m_bSupportRing) { 
		tcpRing();

		colTags = 0; 

		while (!((colTags==TAG_GOODFORMAT) || (colTags==TAG_BADFORMAT))){
		{	
		int n = ReceiveData(&m_Socket, (char*)&colTags, sizeof(DWORD));
			if (n<0){
				CloseSocket();
				UpdateStatus("Disconnect");
				goto error_exit;
			}
		}

		}
		
		if (colTags!=TAG_GOODFORMAT) {
			CloseSocket();
			UpdateStatus("Decline");
			goto error_exit;
		}

		
	}
*/

	while(!m_bQuit)
	{
		if(m_bStored)
		{
			count++;
			if(count >= 50)
			{
				count = 0;
				if(!TestDiskSpace())
				{
					UpdateStatus(DISCONNECT);
					goto error_exit8;
				}
			}
		}

		if(!TAGRequest())
		{
			if(!m_bStored)
			{
				CheckExit();
				CloseSocket(FALSE);
				MakeSleep1();
				goto open_connection;
			}
			goto error_exit9;
		}

		CheckExit();
		colTags = 0;
		if(ReceiveData(&m_Socket, (char*)&colTags, sizeof(DWORD)) < 0)
		{
			if(!m_bStored)
			{
				CheckExit();
				CloseSocket(FALSE);
				MakeSleep1();
				goto open_connection;
			}
			goto error_exit10;
		}

		if(colTags == 0xBADF)
		{
			CheckExit();
			CloseSocket(FALSE);
			goto open_connection;
		}

		cycle = 0;

		if(m_bStored)
		{
#ifdef _JNC_REC_
			if(m_bGetRecords)
			{
				DWORD dwTag, dwLen;

				if(colTags != 1)
				{
					goto error_exit;
				}

				CheckExit();
				dwTag = 0;
				if(ReceiveData(&m_Socket, &dwTag, 4) < 0)
				{
					goto error_exit;
				}

				if(dwTag != TAG_TIMEMARK)
				{
					goto error_exit;
				}

				CheckExit();
				dwLen = 0;
				if(ReceiveData(&m_Socket, &dwLen, 4) < 0)
				{
					goto error_exit;
				}

				CheckExit();
				// get list of records to play
				if(ReceiveData(&m_Socket, m_pData, dwLen) <= 0)
				{
					goto error_exit;
				}

				CheckExit();
				returnRecords(m_pData, dwLen);

				goto error_exit;
			}
#endif //def _JNC_REC_
			m_dwCountByte = 0;
		}
		while(colTags)
		{
			CheckExit();
			if(!TAGReceive())
			{
				if(!m_bStored)
				{
					CheckExit();
					CloseSocket(FALSE);
					MakeSleep1();
					goto open_connection;
				}
				goto error_exit11;
			}
			--colTags;
		}
#ifdef VC70
		if(m_bStored)
		{
			if(m_file)
			{
				int dwTag = TAG_SIZE;
				slFile->Lock();
				fwrite(&dwTag, sizeof(DWORD), 1, m_file);
				fwrite(&m_dwCountByte, sizeof(DWORD), 1, m_file);
				CVideoPlayer* pvp = getPlayer();
				if(pvp) pvp->m_lCurrentStoredData = ftell(m_file);
				slFile->Unlock();
			}
		}
#endif //def VC70
	}
exit:
_log("m_bQuit="<<m_bQuit<<"\n");
	m_info.resetAddress();
#ifdef _JNC_REC_
	CloseSocket(!m_bGetRecords);
	m_bGetRecords = FALSE;
#else //def _JNC_REC_
	CloseSocket(TRUE);
#endif //def _JNC_REC_
	closeChannel();
	if(slFile && slFile->IsLocked()) slFile->Unlock();
	UpdateStatus(FINISH);
	mesOut(_T("VC70SocketThread.cpp"), __LINE__, _T("normally leaving ThreadFunc()"));
_log("!QUIT!\n");
	return 0;

error_exit11:
	errcnt++;
error_exit10:
	errcnt++;
error_exit9:
	errcnt++;
error_exit8:
	errcnt++;
error_exit7:
	errcnt++;
error_exit6:
	errcnt++;
error_exit5:
	errcnt++;
error_exit4:
	errcnt++;
error_exit3:
	errcnt++;
error_exit2:
	errcnt++;
error_exit1:
	errcnt++;
error_exit:
	m_info.resetAddress();
#ifdef _JNC_REC_
	CloseSocket(!m_bGetRecords);
	m_bGetRecords = FALSE;
#else //def _JNC_REC_
	CloseSocket();
#endif //def _JNC_REC_
	closeChannel();
	if(slFile && slFile->IsLocked()) slFile->Unlock();
	UpdateStatus(FAIL, errcnt);
	mesOut(_T("VC70SocketThread.cpp"), __LINE__, _T("leaving ThreadFunc() with error:%i"), errcnt);

	return errcnt;
}

DWORD CVC70SocketThread::OnTerminate()
{
	mesOut(_T("VC70SocketThread.cpp"), __LINE__, _T("OnTerminate()"));

	CEvent* pevent = getRefreshEvent();
	if(pevent) pevent->SetEvent();

	return DEFAULT_TERMINATION_TIMEOUT;
}

void CVC70SocketThread::OnCriticalTerminate()
{
	mesOut(_T("VC70SocketThread.cpp"), __LINE__, _T("OnCriticalTerminate(), m_bTerminated=%i"), m_bTerminated);

	m_info.resetAddress();
	CloseSocket(FALSE);
	closeChannel();
}

void CVC70SocketThread::closeChannel()
{
	if(m_bStored)
	{
		if(m_file)
		{
			fclose(m_file);
			m_file = NULL;
		}
	}

	if(!m_bStored)
	{
		enterCritical();
		if(getYComponent()) if(*(getYComponent())) { delete[] *(getYComponent()); *(getYComponent()) = NULL; }
		if(getUComponent()) if(*(getUComponent())) { delete[] *(getUComponent()); *(getUComponent()) = NULL; }
		if(getVComponent()) if(*(getVComponent())) { delete[] *(getVComponent()); *(getVComponent()) = NULL; }
		leaveCritical();
	}
}

BOOL CVC70SocketThread::TestDiskSpace()
{
	int nTotalFreeSpace = 0;

	if(GetCurrentDirectory(255, m_szBuffer))
	{
		FARPROC pGetDiskFreeSpaceEx = GetProcAddress(GetModuleHandle("kernel32.dll"),
													 "GetDiskFreeSpaceExA");
		if(pGetDiskFreeSpaceEx)
		{
			if(GetDiskFreeSpaceEx(m_szBuffer,
								  (PULARGE_INTEGER)&m_i64FreeBytesToCaller,
								  (PULARGE_INTEGER)&m_i64TotalBytes,
								  (PULARGE_INTEGER)&m_i64FreeBytes))
			{
				nTotalFreeSpace = (int)(m_i64FreeBytesToCaller / (1024 * 1024));
			}
		}
		else
		{
			if(GetDiskFreeSpace(NULL, 
								&m_dwSectPerClust, 
								&m_dwBytesPerSect,
								&m_dwFreeClusters, 
								&m_dwTotalClusters))
			{
				nTotalFreeSpace = (int)(m_dwSectPerClust * m_dwBytesPerSect * m_dwFreeClusters / (1024 * 1024));
			}
		}
	}
	else
	{
		if(GetDiskFreeSpace(NULL, 
							&m_dwSectPerClust, 
							&m_dwBytesPerSect,
							&m_dwFreeClusters, 
							&m_dwTotalClusters))
		{
			nTotalFreeSpace = (int)(m_dwSectPerClust * m_dwBytesPerSect * m_dwFreeClusters / (1024 * 1024));
		}
	}
	if((nTotalFreeSpace < MIN_FREE_SPACE) && nTotalFreeSpace)
	{
		errOut(_T("VC70SocketThread.cpp"), __LINE__, _T("Warning! Hard disk is full."));
		return FALSE;
	}
	return TRUE;
}

void CVC70SocketThread::resetStoredVideo()
{
#ifdef VC70
	if(!slFile)
	{
		CSyncObject* flock = getFileLock();
		slFile = (flock) ? new CSingleLock(flock) : NULL;
	}

	if(slFile)
	{
		slFile->Lock();
		if(m_file)
		{
			fclose(m_file);
		}

		CString hp = getHomePath();
		hp += "\\";
		hp += STORED_VIDEO_FILENAME;
		m_file = fopen(hp, "wb"); // to truncate data

		if(m_file)
		{
			fclose(m_file);
		}
		m_file = NULL;
		slFile->Unlock();
	}
#endif //def VC70
}

BOOL CVC70SocketThread::bindSocket(DWORD addr, int nTCPPort/* = 777*/)
{
	sockaddr_in client;
	in_addr a;
	//Fixes Bug in MFC Socket Impl
	LINGER linger = { 1,0 };
	BOOL bReuse = TRUE;	// reset port/address
	a.S_un.S_addr = addr;

	// create new socket
	m_Socket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
	if(m_Socket == INVALID_SOCKET)
	{
		if(!m_bQuit)
		{
			HWND hwnd = getWindow();
			if(hwnd) ShowSockError(hwnd, "Unable to create socket.", inet_ntoa(a));
		}
		return FALSE;
	}

	setsockopt(m_Socket, SOL_SOCKET, SO_LINGER, (const char FAR *)&linger, sizeof(linger));
//	setsockopt(m_Socket, SOL_SOCKET, SO_REUSEADDR, (const char FAR *)&bReuse, sizeof(BOOL));

	memset(&client, 0, sizeof(client));
	client.sin_family = PF_INET;
	client.sin_port = htons(nTCPPort + 200);// start with +200
	client.sin_addr.s_addr = INADDR_ANY;

	// find and bin local port to bind to
	for(int i = 0; i < 100; i++)
	{
		if(m_bQuit) break;

		//int option=1;setsockopt (m_Socket, SOL_SOCKET, SO_REUSEADDR, (char*)&option, sizeof (option));
		if(bind(m_Socket, (sockaddr*)&client, sizeof(client)) != INVALID_SOCKET)
		{
			return TRUE;
		}
		TRACE3("%x - CVC70SocketThread::bindSocket(%s) error: %i\n", this, inet_ntoa(a), WSAGetLastError());

		client.sin_port = htons(ntohs(client.sin_port) + 1);
	}

	if(!m_bQuit)
	{
		HWND hwnd = getWindow();
		if(hwnd) ShowSockError(hwnd, "Unable to bind socket.", inet_ntoa(a));
	}

	return FALSE;
}

BOOL CVC70SocketThread::connectToServer(DWORD addr, int port)
{
	sockaddr_in server;

	memset(&server, 0, sizeof(server));
	server.sin_family = PF_INET;
	server.sin_addr.s_addr = addr;
	server.sin_port = htons(port);

#ifdef _J707_ //merge from VC-X:BUGFIX #005
	while (TRUE)
#else
 	for(int i = 0; i < MAX_CONNECT_RETRY; i++)
#endif
	{
		if(m_bQuit) break;

		Sleep(10);
		if(connect(m_Socket, (sockaddr*)&server, sizeof(server)) != SOCKET_ERROR)
		{
			int sz = sizeof(m_sa);
			getsockname(m_Socket, (sockaddr*)&m_sa, &sz);
			return TRUE;
		}
		TRACE3("%x - CVC70SocketThread, error: %i in connectToServer(%s,...)\n", this, WSAGetLastError(), inet_ntoa(server.sin_addr));
	}

	if(!m_bQuit)
	{
		HWND hwnd = getWindow();
		if(hwnd) ShowSockError(hwnd, "Unable to connect to server.", inet_ntoa(server.sin_addr));
	}

	return FALSE;
}

void CVC70SocketThread::reformatCallString(LPSTR str, CInfo* info)
{
	char site[128];
	char building[128];
	char room[128];
	char camera[128];
	char password[128];
	int id = 0;
	char ip[128];
	int port = 0;
	int FrameRate = 0;
	int QualityRange = 0;
	int Bitrate = 0;
	int Width = 0;
	int Height = 0;

	sscanf(str, "%s %s %s %s %i %s %s %i %i %i %i %i %i",
		   site, building, room, camera, &id, password, ip, &port,
		   &FrameRate, &QualityRange, &Bitrate, &Width, &Height);

	strcpy(ip, inet_ntoa(m_sa.sin_addr));
	sprintf(str, "%s %s %s %s %i %s %s %i %i %i %i %i %i",
			site, building, room, camera, id, password, ip, port,
			FrameRate, QualityRange, info->m_iBitrate, Width, Height);
}
